package com.seleniti.enpareja.web.desktop.client.ui.view;

import static com.google.gwt.dom.client.BrowserEvents.CHANGE;
import static com.google.gwt.dom.client.BrowserEvents.CLICK;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.ioc.commons.impl.gwt.client.ui.HasEnabledWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasTextWrapper;
import org.ioc.commons.impl.gwt.client.ui.HasValueWrapper;
import org.ioc.commons.ui.HasText;
import org.ioc.commons.ui.HasValue;

import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.AbstractSafeHtmlCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.OptionElement;
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
import com.google.gwt.text.shared.SafeHtmlRenderer;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellList;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CustomPushButton;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.DialogBox.CaptionImpl;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ToggleButton;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.NoSelectionModel;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.google.gwt.view.client.SingleSelectionModel;
import com.seleniti.enpareja.controller.model.integration.database.domain.Product;
import com.seleniti.enpareja.controller.model.integration.database.domain.Store;
import com.seleniti.enpareja.controller.ui.view.PendingShoppingView;
import com.seleniti.enpareja.controller.ui.view.PendingShoppingView.Action;
import com.seleniti.enpareja.controller.ui.view.PendingShoppingView.Event;
import com.seleniti.enpareja.controller.ui.view.PendingShoppingView.Operation;
import com.seleniti.enpareja.controller.ui.view.PendingShoppingView.Presenter;
import com.seleniti.enpareja.controller.ui.view.PendingShoppingViewPresenter;
import com.seleniti.enpareja.web.desktop.client.ui.cell.EnParejaTemplate;
import com.seleniti.enpareja.web.desktop.client.ui.resources.CommonResources;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker;
import com.seleniti.enpareja.web.desktop.client.ui.widget.Checker.SelectionMode;
import com.seleniti.enpareja.web.desktop.client.ui.widget.PendingMark;

public class PendingShoppingViewWidget extends EnParejaBaseView<Event, Operation, Action, Presenter> implements
		PendingShoppingView {

	private static TemplateUiBinder uiBinder = GWT.create(TemplateUiBinder.class);

	interface TemplateUiBinder extends UiBinder<Widget, PendingShoppingViewWidget> {
	}

	private static EnParejaTemplate template = GWT.create(EnParejaTemplate.class);

	public static interface Style extends CssResource {
		String container();

		String columnsHeader();

		String done();

		String pending();

		String rightCol();

		String leftCol();

		String expand();
		
		String down();
	}

	@UiField(provided = true)
	CellList<Product> cellList;

	@UiField
	Style style;

	@UiField
	CustomPushButton btnAdd;

	@UiField
	ToggleButton btnModify;

	@UiField
	ToggleButton btnDelete;

	@UiField
	CommonResources common;

	@UiField(provided = true)
	Checker<Product> checker;

	@UiField
	ToggleButton weHaveFilter;

	@UiField
	Button where;
	
	@UiField
	PendingMark pendingMark;
	
	@UiField
	Panel markContainer;

	private ListDataProvider<Product> listDataProvider;

	private boolean loading;

	private ProvidesKey<Product> keyProvider;

	private ActionMode actionMode;

	private boolean enabled = true;

	private DialogBox whereFilterDialog;

	private SingleSelectionModel<Store> whereFilterSelectionModel;

	private final List<Store> storeList = new ArrayList<Store>();

	public PendingShoppingViewWidget() {
		this.presenter = new PendingShoppingViewPresenter(this, eventbus, operationManager, actionController);

		this.keyProvider = new ProvidesKey<Product>() {

			@Override
			public Object getKey(Product item) {
				return item != null ? item.getProductId() : null;
			}
		};
		SafeHtmlRenderer<Product> renderer = new AbstractSafeHtmlRenderer<Product>() {

			@Override
			public SafeHtml render(Product object) {
				if (object != null) {
					SafeHtml listElement = null;
					String curStoreName = object.getWhere().getName();
					SafeHtmlBuilder shb = new SafeHtmlBuilder();
					shb.append(template.option("0", "-- Añadir nuevo lugar --"));
					for (Store store : storeList()) {
						String name = store.getName();
						if (name.equals(curStoreName)) {
							shb.append(template.optionSelected(store.getStoreId(),
									name.isEmpty() ? "-- Lugar sin definir --" : name));
						} else {
							shb.append(template.option(store.getStoreId(), name.isEmpty() ? "-- Lugar sin definir --"
									: name));
						}
					}

					SafeHtml options = shb.toSafeHtml();
					SafeHtml select = template.select(style.expand(), options);
					if (!object.isPending()) {
						// String extText = null;
						// if (object.getMadeBy() != null) {
						// String madeBy = "--";
						// switch (object.getMadeBy()) {
						// case BOTH:
						// madeBy = "ambos";
						// break;
						// case MEMBER_1:
						// madeBy = member1().getText() != null ?
						// member1().getText() : "tí";
						// break;
						// case MEMBER_2:
						// madeBy = member2().getText() != null ?
						// member2().getText() : "tu pareja";
						// break;
						// }
						// extText = Format.substitute("Hecho por {0} el {1}",
						// madeBy,
						// DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT).format(object.getLastTime()));
						// } else {
						// extText = Format.substitute("Hecho el {0}",
						// DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT).format(object.getLastTime()));
						// }
						// SafeHtml ext =
						// template.divImg(common.tick32x32().getSafeUri(),
						// style.done(),
						// SafeHtmlUtils.fromString(extText));
						listElement = template.listElement(common.style().tri_left() + " " + style.done(),
								object.getText());

					} else {
						// SafeHtml ext = template.div(style.pending() + " " +
						// common.style().yellowButton(),
						// SafeHtmlUtils.fromString("Pendiente"));
						listElement = template.listElement(common.style().tri_right() + " " + style.pending(),
								object.getText());
					}
					return template
							.table2Colums(style.expand(), style.leftCol(), listElement, style.rightCol(), select);
				} else {
					return SafeHtmlUtils.fromString("-- no element --");
				}
			}
		};
		this.cellList = new CellList<Product>(new AbstractSafeHtmlCell<Product>(renderer, "click", "change") {

			@Override
			protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) {
				if (data != null) {
					sb.append(data);
				}
			}

			public void onBrowserEvent(Context context, Element parent, Product value, NativeEvent event,
					ValueUpdater<Product> valueUpdater) {
				super.onBrowserEvent(context, parent, value, event, valueUpdater);
				EventTarget eventTarget = event.getEventTarget();
				if (!Element.is(eventTarget)) {
					return;
				}
				if (parent.getFirstChildElement().isOrHasChild(Element.as(eventTarget))) {
					if (CLICK.equals(event.getType())) {
						// Ignore clicks that occur outside of the main
						// element.
						if (Element.as(eventTarget).getTagName().equalsIgnoreCase(SelectElement.TAG)) {
							// Ignore
						} else if (valueUpdater != null) {
							valueUpdater.update(value);
						}
					} else if (CHANGE.equals(event.getType())) {
						SelectElement se = SelectElement.as(Element.as(eventTarget));
						int idx = se.getSelectedIndex();
						if (idx >= 0) {
							OptionElement selected = se.getOptions().getItem(idx);
							if (selected.getValue().equals("0")) {
								presenter.onAddAndSetNewStore(value);
							} else {
								presenter.onStoreChanged(value, selected.getValue());
							}
						}
					}
				}
			}

		}, keyProvider);

		this.cellList.setValueUpdater(new ValueUpdater<Product>() {

			@Override
			public void update(Product value) {
				if (actionMode != null) {
					switch (actionMode) {
					case MODIFY_ELEMENT:
						modifyElement(value);
						break;
					case SWITCH_LIST:
						switchElementFromList(value);
						break;
					case DELETE_ELEMENT:
					default:
						/*
						 * Nothing to do
						 */
					}
				}
			}
		});

		HTML empty = new HTML("Ningún elemento en tu lista");
		this.cellList.setEmptyListWidget(empty);

		this.cellList.setSelectionModel(new NoSelectionModel<Product>(keyProvider));

		this.listDataProvider = new ListDataProvider<Product>();
		this.listDataProvider.addDataDisplay(cellList);

		this.checker = new Checker<Product>(50);
		this.checker.addDataDisplay(cellList);

		initWidget(uiBinder.createAndBindUi(this));

		empty.setStyleName(common.style().emptyList());
	}

	private Set<String> getStoreNames() {
		Set<String> names = new HashSet<String>();

		for (Store store : this.storeList) {
			names.add(store.getName());
		}

		return names;
	}

	@Override
	public List<Product> productsList() {
		return this.listDataProvider.getList();
	}

	@UiHandler("btnModify")
	void onChangeButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(enabled ? ActionMode.MODIFY_ELEMENT : ActionMode.SWITCH_LIST);

		if (enabled) {
			btnDelete.setDown(false);
		}
	}

	@UiHandler("btnDelete")
	void onDeleteButtonToggled(ValueChangeEvent<Boolean> evt) {
		boolean enabled = Boolean.TRUE.equals(evt.getValue());
		this.setActionMode(enabled ? ActionMode.DELETE_ELEMENT : ActionMode.SWITCH_LIST);

		if (enabled) {
			btnModify.setDown(false);
		}
	}

	@UiHandler("btnAdd")
	void onAddElementButtonClicked(ClickEvent evt) {
		this.addNewElement();
	}

	@UiHandler("checker")
	void onCheckerSelectionChanged(SelectionChangeEvent evt) {
		presenter.onSelectionChanged();
	}

	@Override
	public void addNewElement() {
		this.presenter.onAddNewElement();
	}

	@Override
	public void setLoading(boolean loading) {
		this.loading = loading;
		btnAdd.setEnabled(!loading);
		btnModify.setEnabled(!loading);
		btnDelete.setEnabled(!loading);
	}

	@Override
	public boolean isLoading() {
		return this.loading;
	}

	@Override
	public void setActionMode(ActionMode mode) {
		this.actionMode = mode;
		switch (mode) {
		case DELETE_ELEMENT:
			checker.setSelectionMode(SelectionMode.Multiselection);
			checker.setVisible(true);
			btnDelete.setDown(true);
			btnModify.setDown(false);
			break;
		case SWITCH_LIST:
			checker.setVisible(false);
			btnDelete.setDown(false);
			btnModify.setDown(false);
			break;
		case MODIFY_ELEMENT:
			checker.setSelectionMode(SelectionMode.SingleSelection);
			checker.setVisible(true);
			btnDelete.setDown(false);
			btnModify.setDown(true);
			break;
		}

		this.presenter.onActionModeChanged();
	}

	@Override
	public ActionMode getActionMode() {
		return actionMode;
	}

	public void switchElementFromList(Product value) {
		presenter.onSwitchElementFromList(value);
	}

	@Override
	public Set<Product> getSelection() {
		Set<Product> selection = new HashSet<Product>();
		boolean selected[] = this.checker.getSelection();
		for (int i = 0; i < selected.length; i++) {
			boolean elmSelected = selected[i];

			if (elmSelected) {
				selection.add(this.productsList().get(i));
			}
		}

		return selection;
	}

	@Override
	public boolean isEnabled() {
		return this.enabled;
	}

	@Override
	public void setEnabled(boolean enabled) {
		this.enabled = true;
		HasEnabledWrapper.setEnabled(enabled, btnAdd, btnDelete, btnModify);
		this.setTitle(enabled ? "" : "¡Asociate a tu pareja antes de empezar a usar todas las opciones!");
	}

	@Override
	public void modifyElement(Product element) {
		this.presenter.onModifyElement(element);
	}

	@Override
	public HasValue<Boolean> onlyPendingFilter() {
		return HasValueWrapper.from(weHaveFilter, true, this);
	}

	@UiHandler("weHaveFilter")
	void onWeHaveFilter(ValueChangeEvent<Boolean> evt) {
		this.presenter.onBoughtOrPendingButtonToggled();
		this.markContainer.setStyleName(style.down(), evt.getValue());
	}

	@UiHandler("weHaveFilter2")
	void onWeHaveFilter2ButtonClicked(ClickEvent evt) {
		weHaveFilter.setValue(!weHaveFilter.getValue(), true);
	}

	@UiHandler("where")
	void onWhereButtonClicked(ClickEvent evt) {
		this.showWhereFilter();
	}

	@Override
	public void showWhereFilter() {
		if (this.whereFilterDialog == null) {
			CaptionImpl caption = new CaptionImpl();
			caption.setHTML(template.caption(common.style().caption(), SafeHtmlUtils.fromString("Filtrar lugar:")));
			this.whereFilterDialog = new DialogBox(false, false, caption);
			CellList<Store> storeList = new CellList<Store>(new AbstractCell<Store>() {

				@Override
				public void render(com.google.gwt.cell.client.Cell.Context context, Store value, SafeHtmlBuilder sb) {
					if (value != null) {
						String name = value.getName();
						if (name == null || name.isEmpty()) {
							sb.append(template.div(common.style().commonText(), "-- Aún sin definir --"));
						} else {
							sb.append(template.div(common.style().commonText(), name));
						}
					} else {
						sb.append(template.div(common.style().commonText(), "-- Cualquier lugar --"));
					}
				}
			});
			this.whereFilterSelectionModel = new SingleSelectionModel<Store>();
			storeList.setSelectionModel(whereFilterSelectionModel);
			ListDataProvider<Store> storeListProvider = new ListDataProvider<>();
			storeListProvider.addDataDisplay(storeList);

			storeListProvider.getList().add(null);

			storeListProvider.getList().addAll(storeList());

			this.whereFilterDialog.setWidget(storeList);
			this.whereFilterDialog.setSize("100%", "100%");
			this.whereFilterDialog.addStyleName(common.style().dialog());

			whereFilterSelectionModel.addSelectionChangeHandler(new Handler() {

				@Override
				public void onSelectionChange(SelectionChangeEvent event) {
					presenter.onWhereFilterChanged();
				}
			});
		}

		whereFilterDialog.center();

	}

	@Override
	public List<Store> storeList() {
		return this.storeList;
	}

	@Override
	public void hideWhereFilter() {
		whereFilterDialog.hide();
	}

	@Override
	public Store getWhereFilter() {
		return this.whereFilterSelectionModel != null ? this.whereFilterSelectionModel.getSelectedObject() : null;
	}

	@Override
	public HasText whereFilterText() {
		return HasTextWrapper.from(where, this);
	}

	@Override
	public HasValue<Integer> pendingCounter() {
		return this.pendingMark;
	}

}
